<?php
/**
 * @license http://www.gnu.org/licenses/gpl.html GNU GPL, version 3 or later
 * @package FrontPress
 * @subpackage Thing
 */

/**
 * Used to validate FP_Thing objects.
 *
 * @since   2010-07-27-r15
 * @version $Id: class.fp-validation-rules.php 32 2010-07-29 16:25:18Z Nightgunner5 $
 */
class FP_Validation_Rules {
	private $thing;
	private $fields;
	private $fields_regexp;
	private $rules;

	function __construct( FP_Thing $thing ) {
		$this->thing = $thing;
		$this->fields = get_object_vars( $thing );
		unset( $this->fields['__table'], $this->fields['__class'] );
		$this->fields = array_keys( $this->fields );
		$this->fields_regexp = '(' . str_replace( '/', '\\/', implode( '|', array_map( 'preg_quote', $this->fields ) ) ) . ')';
		$this->rules = array();
	}

	function __call( $method, $args ) {
		$positive = '(?:should|must|has_to)';
		$negative = '(?:cannot|cant|can_not|must_not|should_not)';
		if ( preg_match( '/^' . $this->fields_regexp . '_' . $negative . '_be_(?:changed|modified)$/i', $method, $field ) ) {
			$this->rules[$field[1]][] = new _FP_Validation_Rule_No_Modify();
			return;
		}
		if ( preg_match( '/^' . $this->fields_regexp . '_' . $positive . '_match_regexp?$/i', $method, $field ) ) {
			$this->rules[$field[1]][] = new _FP_Validation_Rule_Match_Regexp( $args[0] );
			return;
		}
		if ( preg_match( '/^' . $this->fields_regexp . '_' . $positive . '_(?:be|(?:match|equal)(?:_exactly|_string)?)$/i', $method, $field ) ) {
			$this->rules[$field[1]][] = new _FP_Validation_Rule_Equal( $args[0] );
			return;
		}
		if ( preg_match( '/^' . $this->fields_regexp . '_is_a_thing$/i', $method, $field ) ) {
			$this->rules[$field[1]][] = new _FP_Validation_Rule_Thing( $args[0] );
			return;
		}
		if ( preg_match( '/^' . $this->fields_regexp . '_' . $positive . '_be_(?:an?_)?(empty|int|positive_int|between|between_exclusive)$/i', $method, $field ) ) {
			$this->rules[$field[1]][] = new _FP_Validation_Rule_Misc_Positive( $field[2], $args );
			return;
		}
		if ( preg_match( '/^' . $this->fields_regexp . '_' . $negative . '_be_(?:an?_)?(empty|int|positive_int|between|between_exclusive)$/i', $method, $field ) ) {
			$this->rules[$field[1]][] = new _FP_Validation_Rule_Misc_Negative( $field[2], $args );
			return;
		}

		trigger_error( 'Call to undefined method FP_Validation_Rules::' . $method . '()', E_USER_ERROR );
	}

	function validate( $new, $old ) {
		foreach ( $this->rules as $field => $rules ) {
			foreach ( $rules as $rule ) {
				if ( !$rule->validate( $new[$field], $old[$field] ) ) {
					return false;
				}
			}
		}
		return true;
	}

	function validate_new( $new ) {
		foreach ( $this->rules as $field => $rules ) {
			foreach ( $rules as $rule ) {
				if ( !$rule->validate_new( $new[$field] ) ) {
					return false;
				}
			}
		}
		return true;
	}
}

/**#@+ @access private */
interface FP_Validation_Rule {
	function validate( $new, $old );
	function validate_new( $new );
}

class _FP_Validation_Rule_No_Modify implements FP_Validation_Rule {
	function validate( $new, $old ) {
		return $new == $old;
	}

	function validate_new( $new ) {
		return true;
	}
}

class _FP_Validation_Rule_Equal implements FP_Validation_Rule {
	private $str;
	function __construct( $str ) {
		$this->str = $str;
	}

	function validate( $new, $old ) {
		return $this->validate_new( $new );
	}

	function validate_new( $new ) {
		return $this->str == $new;
	}
}

class _FP_Validation_Rule_Match_Regexp implements FP_Validation_Rule {
	private $regexp;
	function __construct( $regexp ) {
		$this->regexp = $regexp;
	}

	function validate( $new, $old ) {
		return $this->validate_new( $new );
	}

	function validate_new( $new ) {
		return preg_match( $this->regexp, $new ) > 0;
	}
}

class _FP_Validation_Rule_Thing implements FP_Validation_Rule {
	private $className;

	function __construct( $className ) {
		$this->className = $className;
	}

	function validate( $new, $old ) {
		return $this->validate_new( $new );
	}

	function validate_new( $new ) {
		if ( is_a( $new, 'FP_Thing' ) )
			return true;

		return !!call_user_func( array( $this->className, 'get' ), $new );
	}
}

class _FP_Validation_Rule_Misc_Positive implements FP_Validation_Rule {
	private $type;
	private $args;
	protected $positive = true;
	function __construct( $type, $args ) {
		$this->type = $type;
		$this->args = $args;
	}

	function validate( $new, $old ) {
		return $this->validate_new( $new );
	}

	function validate_new( $new ) {
		switch ( $this->type ) {
			case 'empty':
				$res = empty( $new );
				break;
			case 'int':
				$res = preg_match( '/^[+-]?\d+$/', $new );
				break;
			case 'positive_int':
				$res = preg_match( '/^\d+$/', $new );
				break;
			case 'between':
				$res = $new >= $this->args[0] && $new <= $this->args[1];
				break;
			case 'between_exclusive':
				$res = $new > $this->args[0] && $new < $this->args[1];
				break;
		}
		return !$this->positive xor $res;
	}
}

class _FP_Validation_Rule_Misc_Negative extends _FP_Validation_Rule_Misc_Positive {
	protected $positive = false;
}
/**#@-*/